/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ru.dusty.lab3.write;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import ru.dusty.lab3.raid.utils.RaidConfig;
import ru.dusty.lab3.raid.utils.Utils;

/**
 *
 * @author vadim
 */
public class RaidWrite {
    
    private static final String NAME = "write";
    
    private RaidConfig config;
    
    private String root;
    
    private void writeRaid0(int block, byte[] fileBytes, int firstDisk) throws FileNotFoundException, IOException {
        
        int bytesPart = fileBytes.length / config.getDisksCount(); 
        int index = 0;
        for (int i = firstDisk; i < firstDisk + config.getDisksCount(); i++) {
            RandomAccessFile raf = new RandomAccessFile(
                    new File(root + File.separator + "disk" + i), "rw");
            raf.seek(block * config.getBlockSize());
            if (fileBytes.length - index < 2 * bytesPart) {
                raf.writeShort(fileBytes.length - index);
                raf.write(fileBytes, index, fileBytes.length - index);
            } else {
                raf.writeShort(bytesPart);
                raf.write(fileBytes, index, bytesPart);
                index += bytesPart;
            }
            
            raf.close();
        }
    }
    
    private void writeRaid1(int block, byte[] fileBytes, int disk) throws FileNotFoundException, IOException {
        
        //int diskNumber = block / config.getBlocksCount();
        //long offsetBlock = block % config.getBlocksCount();
        RandomAccessFile raf = new RandomAccessFile(
                new File(root + File.separator + "disk" + disk), "rw");
        raf.seek(block * config.getBlockSize());
        raf.writeShort(fileBytes.length);
        raf.write(fileBytes);
        raf.close();
        
        raf = new RandomAccessFile(
                new File(root + File.separator + "disk" + (disk + 1)), "rw");
        raf.seek(block * config.getBlockSize());
        raf.writeShort(fileBytes.length);
        raf.write(fileBytes);
        raf.close();
    }
    
    private List<byte[]> prepareData(byte[] fileBytes) {
        List<byte[]> result = new ArrayList<byte[]>();
        
        int bytesPart = fileBytes.length / config.getDisksCount(); 
        int index = 0;
        for (int i = 0; i < config.getDisksCount(); i++) {
            if (fileBytes.length - index < 2 * bytesPart) {
                result.add(Arrays.copyOfRange(fileBytes, index, fileBytes.length));
            } else {
                result.add(Arrays.copyOfRange(fileBytes, index, index + bytesPart));
                index += bytesPart;
            }
        }
        
        return result;
    }
    
    private void writeRaid01(int block, byte[] fileBytes) throws FileNotFoundException, IOException {
        writeRaid0(block, fileBytes, 0);
        writeRaid0(block, fileBytes, config.getDisksCount());
    }
    
    private void writeRaid10(int block, byte[] fileBytes) throws FileNotFoundException, IOException {
        List<byte[]> data = prepareData(fileBytes);
        for (int i = 0; i < data.size(); i++) {
            writeRaid1(block, data.get(i), i * 2);
        }
    }
    
    private byte[] readImportFile(String filePath) {
        File file = new File(filePath);
        RandomAccessFile rafFile;
        try {
            rafFile = new RandomAccessFile(file, "r");
            
            short sizeofShort = 2;
            if (rafFile.length() > config.getBlockSize() - sizeofShort) {
                System.out.println(NAME + ": The file size exceeds the size of a block.");
                return null;
            }

            byte[] fileBytes = new byte[(int) rafFile.length()];
            rafFile.read(fileBytes);
            rafFile.close();
            
            return  fileBytes;
        } catch (FileNotFoundException ex) {
            System.out.println(NAME + ": File for recording not found.");
            System.out.println(NAME + ": " + ex.getMessage());
            return null;
        } catch (IOException ex) {
            System.out.println(NAME + ": Error occurred while reading the import file.");
            System.out.println(NAME + ": " + ex.getMessage());
            return null;
        }
    }
    
    public int write(String[] args) {
        
        if (args.length < 3) {
            System.out.println(NAME + ": missing operand after \"" + NAME + "\"");
            System.out.println(NAME + ": use \"" + NAME + " root N file\"");
            return 1;
        }
        
        root = args[0];
        config = new RaidConfig(root);
        
        try {
            config.loadConfig();
        } catch (FileNotFoundException ex) {
            System.out.println(NAME + ": Can not find file with the settings.");
            return 2;
        } catch (IOException ex) {
            System.out.println(NAME + ": Error reading settings file.");
            return 2;
        } catch (NumberFormatException ex) {
            System.out.println(NAME + ": Invalid file format settings.");
            return 2;
        }
        
        if (!config.checkConfig()) return 2;
        
        if (!Utils.checkRaid(root, config)) {
            System.out.println(NAME + ": Some disks are not available or can not be read.");
            System.out.println(NAME + ": Try rebuild raid.");
            return 8;
        }
        
        int block;
        try {
            block = Integer.parseInt(args[1]);
        } catch (NumberFormatException ex) {
            System.out.println(NAME + ": Error parsing number of block.");
            System.out.println(NAME + ": " + ex.getMessage());
            return 3;
        }
        
        if (block < 0 || block >= config.getBlocksCount()) {
            System.out.println(NAME + ": Wrong number of block.");
            return 4;
        }
        
        String filePath = args[2];
        byte[] bytes = readImportFile(filePath);
        
        if (bytes == null) return 7;
        
        System.out.println(NAME + ": Recording...");
        
        try {
            if (config.getRaidLevel().equals(RaidConfig.RAID_LEVEL_01)) {
                writeRaid01(block, bytes);
            } else if (config.getRaidLevel().equals(RaidConfig.RAID_LEVEL_10)) {
                writeRaid10(block, bytes);
            } else {
                System.out.println(NAME + ": RAID level is not supported.");
                return 8;
            }

        } catch (FileNotFoundException ex) {
            System.out.println(NAME + ": Could not find the file.");
            System.out.println(NAME + ": " + ex.getMessage());
            System.out.println(NAME + ": Try rebuild raid.");
            return 5;
        } catch (IOException ex) {
            System.out.println(NAME + ": Error occurred while reading the file.");
            System.out.println(NAME + ": " + ex.getMessage());
            return 6;
        }
        
        System.out.println(NAME + ": Recording is completed successfully.");
        
        return 0;
    }
    
}
